package com.example.supplychainbackend.service.Impl;

import com.example.supplychainbackend.controller.dto.blockchain.UploadInfo;
import com.example.supplychainbackend.controller.dto.design.BOMDto;
import com.example.supplychainbackend.controller.dto.warehouse.*;
import com.example.supplychainbackend.dao.warehouse.*;
import com.example.supplychainbackend.entity.warehouse.*;
import com.example.supplychainbackend.mapper.WarehouseMapper;
import com.example.supplychainbackend.service.BlockchainService;
import com.example.supplychainbackend.service.DesignService;
import com.example.supplychainbackend.service.WarehouseService;

import com.example.supplychainbackend.tool.DateTimeUtils;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@RequiredArgsConstructor
public class WarehouseServiceImpl implements WarehouseService {

    private final InboundBillDao inboundBillDao;
    private final OutboundBillDao outboundBillDao;
    private final StockDao stockDao;
    private final WarehouseMapper mapper;
    private final WarehouseAreaDao warehouseAreaDao;
    private final WarehouseLocationDao warehouseLocationDao;
    private final WarehousePositionDao warehousePositionDao;
    private final BlockchainService blockchainService;

    private final DesignService designService;
    private static final AtomicInteger counter = new AtomicInteger(1);

    @Override
    @Transactional
    public Long createInboundBill(InboundBillDto inboundBillDto) {
        InboundBillEntity entity = mapper.toInboundEntity(inboundBillDto);
        Long inboundId = inboundBillDao.save(entity).getId();

        Long stockId = this.createStockByInbound(inboundId, inboundBillDto);
        entity.setStockId(String.valueOf(stockId));
        entity.setOnChain(false);
        inboundBillDao.save(entity);
        return inboundId;
    }

    @Override
    public InboundBillDto updateInboundBill(Long id, InboundBillDto inboundBillDto) {
        InboundBillEntity entity = mapper.toInboundEntity(inboundBillDto);
        entity.setId(id);
        entity = inboundBillDao.save(entity);
        return mapper.toInboundDto(entity);
    }

    @Override
    public InboundBillDto getInboundBill(Long id) {
        InboundBillEntity entity = inboundBillDao.findById(id).get();
        return mapper.toInboundDto(entity);
    }

    @Override
    public List<InboundBillDto> getAllInboundBill(Long eid) {
        List<InboundBillEntity> entities = inboundBillDao.findAll();
        List<InboundBillDto> list = mapper.toInboundDtoList(entities);
        if (0L != eid) {
            List<InboundBillDto> eList = new ArrayList<>();
            for (InboundBillDto dto :
                    list) {
                if (dto.getEnterpriseId().equals(eid.toString())) {
                    eList.add(dto);
                }
            }
            return eList;
        }
        return list;
    }

    @Override
    public Long deleteInboundBill(Long id) {
        inboundBillDao.deleteById(id);
        return id;
    }


    @Transactional
    public Long createOutboundBill(OutboundBillDto outboundBillDto) {
        OutboundBillEntity entity = mapper.toOutboundEntity(outboundBillDto);
        Optional<StockEntity> optionalStock = stockDao.findById(Long.valueOf(entity.getStockId()));
        if (optionalStock.isEmpty()){
            throw new RuntimeException("stock "+ entity.getStockId() + " not found");
        }
        StockEntity stockEntity = optionalStock.get();
        if (entity.getNumber() > stockEntity.getNumber()){
            throw new RuntimeException("stock "+ entity.getStockId() + " not enough");
        }

        InboundBillEntity inboundBill = inboundBillDao.findById(stockEntity.getInboundId()).get();
        if (entity.getBatch() == null){
            entity.setBatch(inboundBill.getBatch());
        }
        if (entity.getBomCode() == null){
            entity.setBomCode(inboundBill.getBomCode());
        }

        stockEntity.setNumber(stockEntity.getNumber() - entity.getNumber());
        stockDao.save(stockEntity);
        entity.setOnChain(false);
        Long outboundId = outboundBillDao.save(entity).getId();

        return outboundId;
    }

    @Override
    public OutboundBillDto updateOutboundBill(Long id, OutboundBillDto outboundBillDto) {
        OutboundBillEntity entity = mapper.toOutboundEntity(outboundBillDto);
        entity.setId(id);
        entity = outboundBillDao.save(entity);
        return mapper.toOutboundDto(entity);
    }

    @Override
    public OutboundBillDto getOutboundBill(Long id) {
        OutboundBillEntity entity = outboundBillDao.findById(id).get();
        return mapper.toOutboundDto(entity);
    }

    @Override
    public List<OutboundBillDto> getAllOutboundBill(Long eid) {
        List<OutboundBillEntity> entities = outboundBillDao.findAll();
        List<OutboundBillDto> list = mapper.toOutboundDtoList(entities);
        if (0L != eid) {
            List<OutboundBillDto> eList = new ArrayList<>();
            for (OutboundBillDto dto :
                    list) {
                if (dto.getEnterpriseId().equals(eid.toString())) {
                    eList.add(dto);
                }
            }
            return eList;
        }
        return list;
    }

    @Override
    public Long deleteOutboundBill(Long id) {
        outboundBillDao.deleteById(id);
        return id;
    }

    @Override
    public Long createStock(StockDto stockDto) {
        StockEntity entity = mapper.toStockEntity(stockDto);
        return stockDao.save(entity).getId();
    }

    @Override
    public Long createStockByInbound(Long inboundId, InboundBillDto inboundBillDto) {
        StockEntity stockEntity =mapper.toInboundStockEntity(inboundBillDto);
        stockEntity.setInboundId(inboundId);

        Long stockId = stockDao.save(stockEntity).getId();
        BOMDto parentBom;
        try{
            parentBom = designService.getBOMByCode(inboundBillDto.getBomCode());
        }catch (RuntimeException e){
            parentBom = BOMDto.builder()
                    .materialCode("0x00"+ inboundBillDto.getInboundType()).build();
            parentBom.setMaterialName(inboundBillDto.getProduct());
            parentBom.setMaterialDesc("入库物料" + inboundBillDto.getStockId());
        }
        parentBom.setOldMaterialCode(parentBom.getMaterialCode());
        parentBom.setMaterialCode(parentBom.getMaterialCode() + "-" + stockId);
        parentBom.setQuantity(inboundBillDto.getNumber());
        parentBom.setManufacturer(inboundBillDto.getUpstream());
        parentBom.setOrderId(String.valueOf(stockId));

        designService.createBOMnoECN(parentBom);

        return stockId;


    }

    @Override
    public StockDto updateStock(Long id, StockDto stockDto) {
        StockEntity stockEntity =mapper.toStockEntity(stockDto);
        stockEntity.setId(id);
        stockDao.save(stockEntity);
        return mapper.toStockDto(stockEntity);
    }

    @Override
    public StockDto getStock(Long id) {
        StockEntity entity = stockDao.findById(id).get();
        return mapper.toStockDto(entity);
    }

    @Override
    public List<StockDto> getAllStock(Long eid) {
        List<StockEntity> entities = stockDao.findAll();
        List<StockDto> list = mapper.toStockDtoList(entities);
        if (0L != eid) {
            List<StockDto> eList = new ArrayList<>();
            for (StockDto dto :
                    list) {
                if (dto.getEnterpriseId().equals(eid.toString())) {
                    eList.add(dto);
                }
            }
            return eList;
        }
        return list;
    }

    @Override
    public Long deleteStock(Long id) {
        stockDao.deleteById(id);
        return id;
    }

    //库区增删改查
    @Override
    public String createWareHouseArea(WarehouseAreaDto form) {
        form.setId(null);
        return warehouseAreaDao.save(mapper.toWarehouseAreaEntity(form)).getId().toString();
    }

    @Override
    public WarehouseAreaDto updateWareHouseArea(String id, WarehouseAreaDto form) {
        return mapper.toWarehouseAreaDto(warehouseAreaDao.save(mapper.toWarehouseAreaEntity(form)));
    }

    @Override
    public WarehouseAreaDto getWareHouseArea(String id) {
        Optional<WarehouseAreaEntity> res = warehouseAreaDao.findById(Long.valueOf(id));
        return mapper.toWarehouseAreaDto(res.get());
    }

    @Override
    public String deleteWareHouseArea(String id) {
        warehouseAreaDao.deleteById(Long.valueOf(id));
        return id;
    }

    @Override
    public List<WarehouseAreaDto> getAllAreas() {
        return mapper.toWarehouseAreaDtoList(warehouseAreaDao.findAll());
    }

    @Override
    public Long createWarehouseLocation(WarehouseLocationDto dto) {
        dto.setId(null);
        WarehouseLocationEntity entity = mapper.toWarehouseLocationEntity(dto);
        entity = warehouseLocationDao.save(entity);
        WarehouseLocationDto locationDto = mapper.toWarehouseLocationDto(entity);
        //工厂id和订单号初始化
        //创建时间初始化
        locationDto.setCreateTime(DateTimeUtils.getCurrentDateTime());
        //重新存入
        entity = mapper.toWarehouseLocationEntity(locationDto);
        entity = warehouseLocationDao.save(entity);
        return entity.getId();
    }

    @Override
    public WarehouseLocationDto updateWarehouseLocation(Long id, WarehouseLocationDto dto) throws Exception {
        Optional<WarehouseLocationEntity> res = warehouseLocationDao.findById(id);
        WarehouseLocationEntity entity;
        if(res.isPresent())
            entity=res.get();
        else
            throw new Exception("不存在此记录，id = "+id.toString());
        //检查传来的三项是否存在，存在则修改
        if(dto.getWarehouseName()!=null)
            entity.setWarehouseName(dto.getWarehouseName());
        if(dto.getWarehouseNumber()!=null)
            entity.setWarehouseNumber(dto.getWarehouseNumber());
        if(dto.getDescription()!=null)
            entity.setDescription(dto.getDescription());
        entity = warehouseLocationDao.save(entity);
        return mapper.toWarehouseLocationDto(entity);
    }

    @Override
    public Long deleteWarehouseLocation(Long id) {
        warehouseLocationDao.deleteById(id);
        return id;
    }

    @Override
    public WarehouseLocationDto getByIdWarehouseLocation(Long id) throws Exception {
        Optional<WarehouseLocationEntity> res = warehouseLocationDao.findById(id);
        WarehouseLocationEntity entity;
        if(res.isPresent())
            entity=res.get();
        else
            throw new Exception("不存在此记录，id = "+id.toString());
        return mapper.toWarehouseLocationDto(entity);
    }

    @Override
    public List<WarehouseLocationDto> getListWarehouseLocation() {
        List<WarehouseLocationEntity> entities = warehouseLocationDao.findAll();
        return mapper.toWarehouseLocationDtoList(entities);
    }

    @Override
    public Long createWarehousePosition(WarehousePositionDto dto) {
        dto.setId(null);
        WarehousePositionEntity entity = mapper.toWarehousePositionEntity(dto);
        entity = warehousePositionDao.save(entity);
        return entity.getId();
    }

    @Override
    public WarehousePositionDto updateWarehousePosition(Long id, WarehousePositionDto dto) throws Exception {
        WarehousePositionEntity entity = mapper.toWarehousePositionEntity(dto);
        entity.setId(id);
        entity = warehousePositionDao.save(entity);
        return mapper.toWarehousePositionDto(entity);
    }

    @Override
    public Long deleteWarehousePosition(Long id) {
        warehousePositionDao.deleteById(id);
        return id;
    }

    @Override
    public WarehousePositionDto getByIdWarehousePosition(Long id) throws Exception {
        Optional<WarehousePositionEntity> res = warehousePositionDao.findById(id);
        WarehousePositionEntity entity;
        if(res.isPresent())
            entity = res.get();
        else
            throw new Exception("不存在此记录，id = "+id.toString());
        return mapper.toWarehousePositionDto(entity);
    }

    @Override
    public List<WarehousePositionDto> getListWarehousePosition() {
        List<WarehousePositionEntity> entities = warehousePositionDao.findAll();
        return mapper.toWarehousePositionDtoList(entities);
    }

    @Override
    public Long bindWarehousePositionDevice(Long id, BindDeviceReqDto dto) {
        WarehousePositionEntity entity = warehousePositionDao.findById(id).get();
        entity.setDeviceId(dto.getDeviceId());
        entity.setDeviceMAC(dto.getDeviceMAC());
        entity.setDeviceNumber(dto.getDeviceNumber());
        return warehousePositionDao.save(entity).getId();

    }

    @Override
    public UploadInfo uploadOutboundBill(String id) {
        OutboundBillDto outboundBill = this.getOutboundBill(Long.valueOf(id));
        if (null!=outboundBill){
            UploadInfo txInfo = blockchainService.uploadWarehouseOutbound("WarehouseOutbound"+id, outboundBill.toString());
            outboundBill.setOnChain(true);
            outboundBill.setCommitTime(txInfo.getCommitTime());
            outboundBill.setTransactionId(txInfo.getTransactionId());
            outboundBillDao.save(mapper.toOutboundEntity(outboundBill));
            return txInfo;
        }
        throw new RuntimeException("outbound bill not found");
    }

    @Override
    public UploadInfo uploadInboundBill(String id) {
        InboundBillDto inboundBill = this.getInboundBill(Long.valueOf(id));
        if (null!=inboundBill){
            UploadInfo txInfo = blockchainService.uploadWarehouseInbound("WarehouseInbound"+id, inboundBill.toString());
            inboundBill.setOnChain(true);
            inboundBill.setCommitTime(txInfo.getCommitTime());
            inboundBill.setTransactionId(txInfo.getTransactionId());
            inboundBillDao.save(mapper.toInboundEntity(inboundBill));
            return txInfo;
        }
        throw new RuntimeException("inbound bill not found");
    }

}
